﻿#region Using Statements
using System;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

#endregion

namespace NetworkDemo
{
    public class ScreenManager : DrawableGameComponent
    {
        #region Fields
        SpriteBatch spriteBatch;
        ContentManager contentManager;
        GameController gameController;
        Game1 gameModel;
        SpriteFont font;
        Texture2D blankTexture;
        InputState input = new InputState();
        private List<Screen> screens = new List<Screen>();
        List<Screen> screensToUpdate = new List<Screen>();
        bool traceEnabled = true;
        bool isInitialized = false;
        object screenLock = new object();

        #endregion

        #region Properties

        /// <summary>
        /// If true, the manager prints out a list of all the screens
        /// each time it is updated. This can be useful for making sure
        /// everything is being added and removed at the right times.
        /// </summary>
        public bool TraceEnabled
        {
            get { return traceEnabled; }
            set { traceEnabled = value; }
        }

        /// <summary>
        /// The ScreenManager font exists, so screens don't need to generate a default font
        /// </summary>
        public SpriteFont Font
        {
            get { return font; }
        }

        public Game1 GameModel
        {
            get { return gameModel; }
        }

        public GameController GameController
        {
            get { return gameController; }
        }
        /// <summary>
        /// The same spriteBatch is shared by all screens
        /// </summary>
        public SpriteBatch SpriteBatch
        {
            get { return spriteBatch; }
        }
        #endregion

        #region Initialization

        public ScreenManager(Game1 game, GameController theController)
            : base(game)
        {
            gameController = theController;
            game.Components.Add(this);
            gameModel = game;
            

        }

        public override void Initialize()
        {
            base.Initialize();
            isInitialized = true;
            AddScreen(new MainMenu());

            
        }

        protected override void LoadContent()
        {
            contentManager = Game.Content;

            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = contentManager.Load<SpriteFont>("Kooteney");
            blankTexture = contentManager.Load<Texture2D>("blank");
            foreach (Screen screen in screens)
            {
                screen.LoadContent(contentManager);
            }
        }

        protected override void UnloadContent()
        {
            foreach (Screen screen in screens)
            {
                screen.UnloadContent(contentManager);
            }
        }
        #endregion

        #region Draw & Update
        public override void Update(GameTime gameTime)
        {
            // Read the keyboard and gamepad.
            input.Update();

            // Make a copy of the master screen list, to avoid confusion if
            // the process of updating one screen adds or removes others.
            screensToUpdate.Clear();
 
            foreach (Screen screen in screens)
                screensToUpdate.Add(screen);

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            // Loop as long as there are screens waiting to be updated.
            while (screensToUpdate.Count > 0)
            {
                // Pop the topmost screen off the waiting list.
                Screen screen = screensToUpdate[screensToUpdate.Count - 1];

                screensToUpdate.RemoveAt(screensToUpdate.Count - 1);

                // Update the screen.
                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (screen.ScreenState == ScreenState.TransitionOn ||
                    screen.ScreenState == ScreenState.Active)
                {
                    // If this is the first active screen we came across,
                    // give it a chance to handle input.
                    if (!otherScreenHasFocus)
                    {
                        screen.HandleInput(input);

                        otherScreenHasFocus = true;
                    }

                    // If this is an active non-popup, inform any subsequent
                    // screens that they are covered by it.
                    if (!screen.IsPopup)
                        coveredByOtherScreen = true;
                }
            }

            // Print debug trace?
            if (traceEnabled)
                TraceScreens();
        }

        /// <summary>
        /// Prints a list of all the screens, for debugging.
        /// </summary>
        void TraceScreens()
        {
            List<string> screenNames = new List<string>();

            foreach (Screen screen in screens)
                screenNames.Add(screen.GetType().Name);

            Trace.WriteLine(string.Join(", ", screenNames.ToArray()));
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (Screen screen in screens)
            {
                if (screen.ScreenState == ScreenState.Hidden)
                {
                    continue;
                }

                screen.Draw(gameTime);
            }
        }

        #endregion

        #region Public Methods

        public Screen GetScreen(string Name)
        {
            foreach (Screen S in screens)
            {
                if (S.GetType().Name == Name) return S;
            }

            return null;
        }

        public void AddScreen(Screen screen)
        {
            screen.ScreenManager = this;
            if (isInitialized)
                screen.LoadContent(contentManager);

            lock (screens)
            {
                screens.Add(screen);
            }
        }

        public void RemoveScreen(Screen screen)
        {
            if (isInitialized)
                screen.UnloadContent(contentManager);

            lock (this)
            {
                screens.Remove(screen);
                screensToUpdate.Remove(screen);
            }
        }

        /// <summary>
        /// Expose an array holding all the screens. We return a copy rather
        /// than the real master list, because screens should only ever be added
        /// or removed using the AddScreen and RemoveScreen methods.
        /// </summary>
        public Screen[] GetScreens()
        {
            return screens.ToArray();
        }

        /// <summary>
        /// Helper draws a translucent black fullscreen sprite, used for fading
        /// screens in and out, and for darkening the background behind popups.
        /// </summary>
        public void FadeBackBufferToBlack(int alpha)
        {
            Viewport viewport = GraphicsDevice.Viewport;

            spriteBatch.Begin();

            spriteBatch.Draw(blankTexture,
                             new Rectangle(0, 0, viewport.Width, viewport.Height),
                             new Color(0, 0, 0, (byte)alpha));

            spriteBatch.End();
        }
        #endregion


    }
}
